/*
* 文件：WebSocketClient.cs
* 作者：王阳
* 时间：2022-11-11
* 描述：WebSocket网络客户端，WebGL平台
*/
using CSharpX.Network;
using System;

namespace UnityX
{
    /// <summary>
    /// 网络客户端
    /// </summary>
    public class WebSocketClient
    {
        /// <summary>
        /// 连接的服务器IP
        /// </summary>
        public string remoteIP { get { return m_HostNameOrAddress; } }
        /// <summary>
        /// 连接的服务器端口
        /// </summary>
        public int remotePort { get { return m_Port; } }
        /// <summary>
        /// 是否连接
        /// </summary>
        public bool isConnected { get { return m_State == 2; } }
        /// <summary>
        /// 网络超时，毫秒为单位，默认3000
        /// </summary>
        public long timeout
        {
            get
            {
                return m_Timeout / 10000;
            }
            set
            {
                m_Timeout = value * 10000;
            }
        }

        /// <summary>
        /// 消息处理中心
        /// </summary>
        public Message message { get; private set; } = new Message();

        // 实例id
        private int m_Id = 0;
        // 服务端地址
        private string m_HostNameOrAddress;
        // 服务端端口
        private int m_Port;
        // 服务端地址
        private string m_Url;
        // 发送缓冲区
        private byte[] m_SendBuffer;
        // 活跃时间
        private long m_ActiveTime = 0;
        // 网络超时
        private long m_Timeout = 3000 * 10000;
        // 状态：0关闭 1连接中 2已连接
        private int m_State = 0;

        /// <summary>
        /// 创建客户端
        /// </summary>
        /// <param name="hostNameOrAddress"></param>
        /// <param name="port"></param>
        /// <param name="bufferSize"></param>
        public WebSocketClient(string hostNameOrAddress, int port, int bufferSize = NetworkDefined.DefaultBufferSize, bool wss = true)
        {
            m_HostNameOrAddress = hostNameOrAddress;
            m_Port = port;
            if (wss)
            {
                if (m_Port == 443)
                {
                    m_Url = "wss://" + hostNameOrAddress;
                }
                else
                {
                    m_Url = "wss://" + hostNameOrAddress + ":" + port;
                }
            }
            else
            {
                if (m_Port == 80)
                {
                    m_Url = "ws://" + hostNameOrAddress;
                }
                else
                {
                    m_Url = "ws://" + hostNameOrAddress + ":" + port;
                }
            }

            m_SendBuffer = new byte[bufferSize];
        }
        ~WebSocketClient()
        {
            Close();
        }

        internal void OnOpen()
        {
            m_State = 2;
            message.CallConnected();
        }

        // 处理消息
        internal unsafe void OnMessage(byte* buffer)
        {
            message.Handle(buffer);
        }
        internal void OnClose()
        {
            HandleDisconnect();
            //StartConnect();
        }
        internal void OnError(int code)
        {
            message.CallError(new Exception("WebSocket错误码:" + code));
        }

        /// <summary>
        /// 开启
        /// </summary>
        public void Open()
        {
            if (m_State != 0)
                return;

            StartConnect();
        }
        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            if (m_State == 0)
                return;

            WebSocketNative.WebSocketClose(m_Id);
            HandleDisconnect();
            m_State = 0;
        }
        // 开始连接
        private void StartConnect()
        {
            try
            {
                m_Id = WebSocketNative.AllocateInstance(m_Url, this);
                m_State = 1;
                m_ActiveTime = DateTime.UtcNow.Ticks;
            }
            catch (Exception e)
            {
                m_State = 0;
                message.CallError(new Exception("创建服务器连接出错 Exception=" + e.ToString()));
            }
        }
        // 断开
        private void HandleDisconnect()
        {
            if (m_State == 2)
            {
                m_ActiveTime = DateTime.UtcNow.Ticks;
                message.CallDisconnect();
            }
            m_State = 3;
        }
        // 更新
        public void Update()
        {
            switch (m_State)
            {
                case 0://关闭
                    break;
                case 1://连接中
                    if (DateTime.UtcNow.Ticks - m_ActiveTime > m_Timeout)
                    {
                        message.CallError(new Exception("连接服务器超时"));
                        WebSocketNative.WebSocketClose(m_Id);
                        StartConnect();
                    }
                    break;
                case 2://正常
                    {
                        if (DateTime.UtcNow.Ticks - m_ActiveTime > m_Timeout)
                        {
                            Send(new Ping());
                            m_ActiveTime = DateTime.UtcNow.Ticks;
                        }
                    }
                    break;
                case 3://断开
                    if (DateTime.UtcNow.Ticks - m_ActiveTime > m_Timeout)
                    {
                        StartConnect();
                    }
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 发送数据包
        /// </summary>
        /// <param name="pak"></param>
        public unsafe void Send(object pak)
        {
            if (!isConnected)
            {
                message.CallError(new Exception("发送消息出错 cmd=" + pak.GetType().Name + " 错误=未连接服务器"));
                return;
            }

            int len;
            fixed (byte* ptr = m_SendBuffer)
            {
                len = message.EncodingMessage(pak, ptr);
            }

            try
            {
                WebSocketNative.WebSocketSend(m_Id, m_SendBuffer, len);
                m_ActiveTime = DateTime.UtcNow.Ticks;
            }
            catch (Exception e)
            {
                message.CallError(new Exception("发送出错 Exception= " + e.ToString()));
                HandleDisconnect();
                StartConnect();
            }
        }
    }
}